﻿using System;
using System.ComponentModel;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Linq;
using System.Linq;
using System.Xml.Serialization;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Web.UI.WebControls;

namespace SPMetadataSecurity.MetadataSecurityWebPart
{
    [ToolboxItemAttribute(false)]
    public partial class MetadataSecurityWebPart : WebPart
    {
        // Uncomment the following SecurityPermission attribute only when doing Performance Profiling on a farm solution
        // using the Instrumentation method, and then remove the SecurityPermission attribute when the code is ready
        // for production. Because the SecurityPermission attribute bypasses the security check for callers of
        // your constructor, it's not recommended for production purposes.
        // [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Assert, UnmanagedCode = true)]
        public MetadataSecurityWebPart()
        {
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            InitializeControl();
        }

        #region Control Events

        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                if (!Page.IsPostBack)
                {
                    InitializeContentTypeList();
                    InitializeMetadataColumnList();
                    InitializeGroupsList();
                    InitializePermissionLevelsList();                    
                    InitializeAppliedRulesAndPermissions();
                }
                ShowExistingRulesForContentType();
                MessageLabel.Text = string.Empty;
                ErrorMessageLabel.Text = string.Empty;
            }
            catch (Exception ex)
            {
                SetMessage(ex.Message + Environment.NewLine + ex.StackTrace, true);
            }
        }

        private void ShowExistingRulesForContentType()
        {
            MetadataSecuirtyConfiguration config = GetCurrentConfiguration(GetSelectedContentType());
            if(config != null)
            {
                RulesGridView.DataSource = config.ConfigurationRules;
                RulesGridView.DataBind();
            }
            else
            {
                RulesGridView.DataSource = null;
                RulesGridView.DataBind();
            }
        }

        protected void ContentTypeList_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {                
                InitializeMetadataColumnList();
                AppliedRulesDiv.InnerHtml = string.Empty;
                AppliedPermissionsDiv.InnerHtml = string.Empty;
                ViewState["MetadataSecuirtyConfiguration"] = null;
                InitializeAppliedRulesAndPermissions();
            }
            catch (Exception ex)
            {
                SetMessage(ex.Message + Environment.NewLine + ex.StackTrace, true);
            }
        }

        protected void AddConditionButton_Click(object sender, EventArgs e)
        {
            try
            {
                MetadataSecuirtyConfiguration config = GetCurrentConfiguration(GetSelectedContentType());
                Rule rule = null;
                bool isNewRule = false;
                if (config.ConfigurationRules.Count > 0)
                {
                    rule = config.ConfigurationRules[0];
                }
                else
                {
                    isNewRule = true;
                    rule = new Rule();
                }
                rule.Name = RuleNameTextBox.Text.Trim();
                if (rule.Conditions == null || rule.Conditions.Count < 0)
                {
                    rule.Conditions = new List<Condition>();
                }
                Condition condition = new Condition()
                {
                    MetadataColumnName = MetadataColumnList.SelectedItem.Text,
                    MetadataColumnId = MetadataColumnList.SelectedValue,
                    Operator = OperatorList.SelectedValue,
                    MetadataValue = ValueTextBox.Text.Trim()
                };
                rule.Conditions.Add(condition);
                if (isNewRule)
                {
                    config.ConfigurationRules.Add(rule);
                }
                ViewState["MetadataSecuirtyConfiguration"] = config;
                string newRule = "<p>[" + MetadataColumnList.SelectedItem.Text + "]" + " " + OperatorList.SelectedValue + " \"" + ValueTextBox.Text.Trim() + "\"<input type='button' value='Remove'></input></p>";
                if (AppliedRulesDiv.InnerHtml.Contains(newRule))
                {
                    SetMessage("Condition already added.", true);
                }
                else
                {
                    AppliedRulesDiv.InnerHtml = newRule + AppliedRulesDiv.InnerHtml;
                }
            }
            catch (Exception ex)
            {
                SetMessage(ex.Message + Environment.NewLine + ex.StackTrace, true);
            }
        }

        protected void AddPermissionButton_Click(object sender, EventArgs e)
        {
            try
            {
                MetadataSecuirtyConfiguration config = GetCurrentConfiguration(GetSelectedContentType());
                Rule rule = null;
                bool isNewRule = false;
                if (config.ConfigurationRules.Count > 0)
                {
                    rule = config.ConfigurationRules[0];
                }
                else
                {
                    isNewRule = true;
                    rule = new Rule();
                }
                rule.Name = RuleNameTextBox.Text.Trim();
                PermissionAction action = null;
                if (rule.Action == null)
                {
                    action = new PermissionAction();
                }
                else
                {
                    action = rule.Action;
                }
                action.RemoveInheritedPermissions = RemoveInheritedPermissionsCheckBox.Checked;
                action.StopInheritingPermission = StopInheritingPermissionsCheckBox.Checked;
                action.UpdateExistingItems = UpdateExistingItemsCheckBox.Checked;
                if (action.GroupPermissions == null || action.GroupPermissions.Count <= 0)
                {
                    action.GroupPermissions = new List<GroupPermission>();
                }
                GroupPermission gp = new GroupPermission()
                {
                    GroupName = GroupList.SelectedItem.Text,
                    GroupId = GroupList.SelectedValue,
                    PermissionLevelName = PermissionLevelList.SelectedItem.Text,
                    PermissionLevelId = PermissionLevelList.SelectedValue
                };
                action.GroupPermissions.Add(gp);
                rule.Action = action;
                if (isNewRule)
                {
                    config.ConfigurationRules.Add(rule);
                }
                ViewState["MetadataSecuirtyConfiguration"] = config;
                string newPermissionAction = "<p>[" + gp.GroupName + "] <====> [" + gp.PermissionLevelName + "] <input type='button' value='Remove'></input></p>";
                if (AppliedPermissionsDiv.InnerHtml.Contains(newPermissionAction))
                {
                    SetMessage("Permission already added.", true);
                }
                else
                {
                    AppliedPermissionsDiv.InnerHtml = newPermissionAction + AppliedPermissionsDiv.InnerHtml;
                }
            }
            catch (Exception ex)
            {
                SetMessage(ex.Message + Environment.NewLine + ex.StackTrace, true);
            }
        }

        protected void AddRuleButton_Click(object sender, EventArgs e)
        {
            try
            {
                SPContentType selectedContentType = GetSelectedContentType();
                if (selectedContentType != null)
                {
                    MetadataSecuirtyConfiguration config = ViewState["MetadataSecuirtyConfiguration"] as MetadataSecuirtyConfiguration;
                    if (config != null)
                    {
                        SPWeb rootWeb = SPContext.Current.Site.RootWeb;
                        XmlSerializer serializer = new XmlSerializer(typeof(MetadataSecuirtyConfiguration));
                        StringWriter writer = new StringWriter();
                        serializer.Serialize(writer, config);
                        rootWeb.DeleteProperty(selectedContentType.Name + "_Metadata_Security");
                        rootWeb.AddProperty(selectedContentType.Name + "_Metadata_Security", writer.ToString());
                        rootWeb.Update();
                        RemoveEventReceiverFromContentType(selectedContentType);
                        AddEventReceiverToContentType(selectedContentType);
                        SetMessage("Rules Saved Succefully.", false);
                    }
                }
            }
            catch (Exception ex)
            {
                SetMessage(ex.Message + Environment.NewLine + ex.StackTrace, true);
            }
        }

        protected void RemoveRuleButton_Click(object sender, EventArgs e)
        {
            try
            {
                SPContentType selectedContentType = GetSelectedContentType();
                if (selectedContentType != null)
                {
                    SPWeb rootWeb = SPContext.Current.Site.RootWeb;
                    rootWeb.DeleteProperty(selectedContentType.Name + "_Metadata_Security");
                    rootWeb.Update();
                    ViewState["MetadataSecuirtyConfiguration"] = null;
                    SetMessage("Rule removed from the content type", false);
                    InitializeAppliedRulesAndPermissions();
                }
            }
            catch (Exception ex)
            {
                SetMessage(ex.Message + Environment.NewLine + ex.StackTrace, true);
            }
        }

        #endregion

        #region Private Methods       

        private MetadataSecuirtyConfiguration GetCurrentConfiguration(SPContentType selectedContentType )
        {
            MetadataSecuirtyConfiguration config = null;
            if (ViewState["MetadataSecuirtyConfiguration"] != null)
            {
                config = ViewState["MetadataSecuirtyConfiguration"] as MetadataSecuirtyConfiguration;
            }
            else
            {
                if (selectedContentType != null)
                {
                    SPWeb rootWeb = SPContext.Current.Site.RootWeb;
                    string xml = rootWeb.GetProperty(selectedContentType.Name + "_Metadata_Security") as string;
                    if (!string.IsNullOrEmpty(xml))
                    {
                        XmlSerializer serializer = new XmlSerializer(typeof(MetadataSecuirtyConfiguration));
                        StringReader reader = new StringReader(xml);
                        config = serializer.Deserialize(reader) as MetadataSecuirtyConfiguration;
                    }
                    else
                    {
                        config = new MetadataSecuirtyConfiguration() { ConfigurationRules = new System.Collections.Generic.List<Rule>() };
                    }
                }
            }
            //ViewState["MetadataSecuirtyConfiguration"] = config;
            return config;
        }

        private void InitializeAppliedRulesAndPermissions()
        {
            SPContentType selectedContentType = GetSelectedContentType();
            if (selectedContentType != null)
            {
                SPWeb rootWeb = SPContext.Current.Site.RootWeb;
                string xml = rootWeb.GetProperty(selectedContentType.Name + "_Metadata_Security") as string;
                MetadataSecuirtyConfiguration config = null;
                if (!string.IsNullOrEmpty(xml))
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(MetadataSecuirtyConfiguration));
                    StringReader reader = new StringReader(xml);
                    config = serializer.Deserialize(reader) as MetadataSecuirtyConfiguration;
                    ViewState["MetadataSecurityConfiguration"] = config;
                }                
                AppliedRulesDiv.InnerHtml = GetAppliedConditionsString(config);
                AppliedPermissionsDiv.InnerHtml = GetAppliedPermissionString(config);
                if (config != null && config.ConfigurationRules != null
                    && config.ConfigurationRules.Count > 0)
                {
                    var rule = config.ConfigurationRules[0];
                    if (rule.Action != null)
                    {
                        StopInheritingPermissionsCheckBox.Checked = rule.Action.StopInheritingPermission;
                        RemoveInheritedPermissionsCheckBox.Checked = rule.Action.RemoveInheritedPermissions;
                        UpdateExistingItemsCheckBox.Checked = rule.Action.UpdateExistingItems;
                    }
                    else
                    {
                        StopInheritingPermissionsCheckBox.Checked = false;
                        RemoveInheritedPermissionsCheckBox.Checked = false;
                        UpdateExistingItemsCheckBox.Checked = false;
                    }
                }
                else
                {
                    StopInheritingPermissionsCheckBox.Checked = false;
                    RemoveInheritedPermissionsCheckBox.Checked = false;
                    UpdateExistingItemsCheckBox.Checked = false;
                }
            }
        }

        private string GetAppliedConditionsString(MetadataSecuirtyConfiguration config)
        {
            string s = string.Empty;
            if (config != null && config.ConfigurationRules.Count > 0)
            {
                Rule rule = config.ConfigurationRules[0];
                if (rule != null && rule.Conditions != null && rule.Conditions.Count > 0)
                {
                    foreach (Condition condition in rule.Conditions)
                    {
                        s += "<p>[" + condition.MetadataColumnName + "]" + " " + condition.Operator + " \"" + condition.MetadataValue + "\"<input type='button' value='Remove'></input></p>";
                    }
                }
            }
            return s;
        }

        private string GetAppliedPermissionString(MetadataSecuirtyConfiguration config)
        {
            string s = string.Empty;
            if (config != null)
            {
                foreach (Rule rule in config.ConfigurationRules)
                {
                    if (rule.Action != null)
                    {
                        foreach (GroupPermission gp in rule.Action.GroupPermissions)
                        {
                            s += "<p>[" + gp.GroupName + "] <====> [" + gp.PermissionLevelName + "] <input type='button' value='Remove'></input></p>";
                        }
                    }
                }
            }
            return s;
        }

        private void InitializeContentTypeList()
        {
            SPSite site = SPContext.Current.Site;
            var contentTypes = site.RootWeb.ContentTypes;
            if (contentTypes != null && contentTypes.Count > 0)
            {
                ContentTypeList.DataSource = contentTypes;
                ContentTypeList.DataTextField = "Name";
                ContentTypeList.DataValueField = "Id";
                ContentTypeList.DataBind();
                ContentTypeList.Items.Add(new ListItem("Select Content Type...", string.Empty));
            }
        }

        private void SetMessage(string message, bool isError)
        {
            if (isError)
            {
                ErrorMessageLabel.Text = message;
                MessageLabel.Text = string.Empty;
            }
            else
            {
                MessageLabel.Text = message;
                ErrorMessageLabel.Text = string.Empty;
            }
        }

        private void InitializeMetadataColumnList()
        {
            SPContentType selectedContentType = GetSelectedContentType();
            if (selectedContentType != null)
            {
                var metadataFields = selectedContentType.Fields.Cast<SPField>().Where(x => x.Type == SPFieldType.Text);
                MetadataColumnList.DataSource = metadataFields;
                MetadataColumnList.DataTextField = "Title";
                MetadataColumnList.DataValueField = "Id";
                MetadataColumnList.DataBind();
                MetadataColumnList.Items.Add(new ListItem("Select Column...", string.Empty));
            }
        }

        private void InitializePermissionLevelsList()
        {
            SPWeb rootWeb = SPContext.Current.Site.RootWeb;
            SPRoleDefinitionCollection roles = rootWeb.RoleDefinitions;
            PermissionLevelList.DataSource = roles;
            PermissionLevelList.DataTextField = "Name";
            PermissionLevelList.DataValueField = "Id";
            PermissionLevelList.DataBind();
            PermissionLevelList.Items.Add(new ListItem("Select Permission...", string.Empty));
        }

        private void InitializeGroupsList()
        {
            SPWeb rootWeb = SPContext.Current.Site.RootWeb;
            SPGroupCollection groups = rootWeb.Groups;
            GroupList.DataSource = groups;
            GroupList.DataTextField = "Name";
            GroupList.DataValueField = "Id";
            GroupList.DataBind();
            GroupList.Items.Add(new ListItem("Select Group...", string.Empty));
        }

        private SPContentType GetSelectedContentType()
        {
            SPContentType selectedContentType = null;
            SPWeb rootWeb = SPContext.Current.Site.RootWeb;
            if (!string.IsNullOrEmpty(ContentTypeList.SelectedValue))
            {
                selectedContentType = rootWeb.ContentTypes[new SPContentTypeId(ContentTypeList.SelectedValue)];
            }
            return selectedContentType;
        }

        private void RemoveEventReceiverFromContentType(SPContentType contentType)
        {
            if (contentType != null)
            {
                IEnumerable<SPEventReceiverDefinition> eventRecCol = contentType.EventReceivers.Cast<SPEventReceiverDefinition>().Where(x => x.Class == "SPMetadataSecurity.MetadataSecurityEventReceiver");

                if (eventRecCol != null && eventRecCol.Count() > 0)
                {
                    for (int i = 0; i < eventRecCol.Count(); i++)
                    {
                        var eventRec = eventRecCol.ElementAt(i);
                        eventRec.Delete();
                        contentType.Update(true);
                    }
                }                
            }
        }

        private void AddEventReceiverToContentType(SPContentType contentType)
        {
            if (contentType != null)
            {                
                SPEventReceiverDefinition itemAddedEventReceiver = contentType.EventReceivers.Add();
                itemAddedEventReceiver.Synchronization = SPEventReceiverSynchronization.Synchronous;
                itemAddedEventReceiver.Type = SPEventReceiverType.ItemAdded;
                itemAddedEventReceiver.Assembly = Assembly.GetExecutingAssembly().FullName;
                itemAddedEventReceiver.Class = "SPMetadataSecurity.MetadataSecurityEventReceiver"; // typeof(MetadataSecurityEventReceiver).Name;

                itemAddedEventReceiver.Update();

                SPEventReceiverDefinition itemUpdatedEventReceiver = contentType.EventReceivers.Add();
                itemUpdatedEventReceiver.Synchronization = SPEventReceiverSynchronization.Synchronous;
                itemUpdatedEventReceiver.Type = SPEventReceiverType.ItemUpdated;
                itemUpdatedEventReceiver.Assembly = Assembly.GetExecutingAssembly().FullName;
                itemUpdatedEventReceiver.Class = "SPMetadataSecurity.MetadataSecurityEventReceiver"; // typeof(MetadataSecurityEventReceiver).DeclaringType.FullName;
                itemUpdatedEventReceiver.Update();
                contentType.Update(true);
            }
        }
        
        #endregion
    }
}
